#include "server.h"
#include "messagehandler.h"
#include "database.h"
#include "memdata.h"
#include "hdddata.h"
#include "connection.h"
#include <iostream>
#include "protocol.h"

using namespace std;
using namespace client_server;
using namespace protocol;

void parseMessage(Connection* conn, stringstream& ss, Database& db, MessageHandler parser){
	int header;
	ss >> header;

	switch (header){
		case Protocol::COM_LIST_NG: {
			vector<pair <int, string> > ngs = db.listAllNewsGroups();
			parser.sendByte(conn, Protocol::ANS_LIST_NG);
			parser.sendIntParameter(conn, ngs.size());
			
			vector<pair <int, string> >::iterator it;
			for(it = ngs.begin(); it != ngs.end(); it++){
				parser.sendIntParameter(conn, it->first);
				parser.sendStringParameter(conn, it->second);
			}

			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		case Protocol::COM_CREATE_NG: {
			unsigned int n;
			ss >> n;

			string ngName;
			ss >> ngName;
			while(ngName.size() < n){
				string temp;
				ss >> temp;
				ngName += " " + temp;			
			}

			parser.sendByte(conn, Protocol::ANS_CREATE_NG);
			try {
				db.createNewsGroup(ngName);
				parser.sendByte(conn, Protocol::ANS_ACK);
			} catch(NewsGroupDoesNotExistException) {
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_NG_ALREADY_EXISTS);
			}
			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		case Protocol::COM_DELETE_NG:{
			unsigned int index;
			ss >> index;

			parser.sendByte(conn, Protocol::ANS_DELETE_NG);
			try {
				db.deleteNewsGroup(index);
				parser.sendByte(conn, Protocol::ANS_ACK);
			} catch(NewsGroupDoesNotExistException) {
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_NG_DOES_NOT_EXIST);
			}
			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		case Protocol::COM_LIST_ART:{
			unsigned int index;
			ss >> index;

			parser.sendByte(conn, Protocol::ANS_LIST_ART);
			try {
				vector<pair<int, string> > result = db.listAllArticles(index);
				parser.sendByte(conn, Protocol::ANS_ACK);
				parser.sendIntParameter(conn, result.size());

				vector<pair<int, string> >::iterator it;
				for(it = result.begin(); it != result.end(); it++){
					parser.sendIntParameter(conn, it->first);
					parser.sendStringParameter(conn, it->second);
				}
			} catch(NewsGroupDoesNotExistException){
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_NG_DOES_NOT_EXIST);
			}
			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		case Protocol::COM_CREATE_ART:{
			unsigned int index;
			string title;
			string author;
			string text;
			unsigned int titleLength;
			unsigned int authorLength;
			unsigned int textLength;
			ss >> index;

			ss >> titleLength,
			ss >> title;
			while(title.size() < titleLength){
				string temp;
				ss >> temp;
				title += " " + temp;				
			}

			ss >> authorLength;
			ss >> author;
			while(author.size() < authorLength){
				string temp;
				ss >> temp;
				author += " " + temp;				
			}

			ss >> textLength;
			
			string s;
			getline(ss, s);
			text += s;
			while(getline(ss, s)){
				text += "\n" + s;
			}

			parser.sendByte(conn, Protocol::ANS_CREATE_ART);
			try {
				db.createArticle(index, title, author, text);
				parser.sendByte(conn, Protocol::ANS_ACK);
			} catch(NewsGroupDoesNotExistException) {
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_NG_DOES_NOT_EXIST);
			}
			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		case Protocol::COM_DELETE_ART:{
			unsigned int ngIndex;
			unsigned int aIndex;
			ss >> ngIndex;
			ss >> aIndex;
			
			parser.sendByte(conn, Protocol::ANS_DELETE_ART);
			try{
				db.deleteArticle(ngIndex, aIndex);
				parser.sendByte(conn, Protocol::ANS_ACK);
			} catch(NewsGroupDoesNotExistException) {
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_NG_DOES_NOT_EXIST);
			} catch (ArticleDoesNotExistException){
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_ART_DOES_NOT_EXIST);
			}
			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		case Protocol::COM_GET_ART:{
			unsigned int ngIndex;
			unsigned int aIndex;
			ss >> ngIndex;
			ss >> aIndex;

			parser.sendByte(conn, Protocol::ANS_GET_ART);
			try{
				stringstream os;
				db.readArticle(ngIndex, aIndex, os);
				string title;
				string author;
				string text;
				unsigned int titleLength;
				unsigned int authorLength;
				unsigned int textLength;

				os >> titleLength,
				os >> title;
				while(title.size() < titleLength){
					string temp;
					os >> temp;
					title += " " + temp;				
				}

				os >> authorLength;
				os >> author;
				while(author.size() < authorLength){
					string temp;
					os >> temp;
					author += " " + temp;				
				}
				
				os >> textLength;
				os.ignore(2, 'a');
				
				string s;
				getline(os, s);
				text += s;
				while(getline(os, s)){
					text += "\n" + s;
				}
				text = text.substr(0, text.size()-1);

				parser.sendByte(conn, Protocol::ANS_ACK);
				parser.sendStringParameter(conn, title);
				parser.sendStringParameter(conn, author);
				parser.sendStringParameter(conn, text);
			} catch(NewsGroupDoesNotExistException) {
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_NG_DOES_NOT_EXIST);
			} catch(ArticleDoesNotExistException){
				parser.sendByte(conn, Protocol::ANS_NAK);
				parser.sendByte(conn, Protocol::ERR_ART_DOES_NOT_EXIST);
			}
			parser.sendByte(conn, Protocol::ANS_END);

		}break;
		
		default: cout << "Incorrect format on message" << endl;;
    	break;
	}
}

void run(Database& db){
	cout<<"Server started"<<endl;
	Server server(1337);
	MessageHandler parser;

while (true) {
		if(server.isReady()){
			Connection* conn = server.waitForActivity();
			if (conn != 0) {
				try {
					//This part will do all the work
					cout<<"New message from existing connection"<<endl;
					stringstream ss;
					parser.read(conn, ss);

					//Test to see what messagehandler returns
					cout << "Recieved message:" << ss.str() <<endl;

					//Parse the message
					parseMessage(conn, ss, db, parser);
				}
				catch (ConnectionClosedException&) {
					server.deregisterConnection(conn);
					delete conn;
					cout << "Client closed connection" << endl;
				}
			}
			else {
				server.registerConnection(new Connection);
				cout << "New client connects" << endl;
			}
		}
	}
}

int main(int argc, char* argv[]){	
	if (argc < 2){
		cerr << "Usage: " << argv[0] << " mdb|hdd" << endl;
		return 1;
	}

	string arg = argv[1];
	if(arg == "mdb"){
		Memdata db;
		run(db);
	} else if(arg == "hdd"){
		HDDdata db;
		run(db);
	} else {
		cerr << "Usage: " << argv[0] << " mdb|hdd" << endl;
		return 1;
	}
}
